package com.fishingwithme.domain.circle;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.fishingwithme.domain.common.Location;
import com.fishingwithme.infrastructure.dtos.circle.CircleGeoInfoDto;
import com.fishingwithme.infrastructure.dtos.circle.SpotInCircleDto;
import com.fishingwithme.infrastructure.mapper.circle.CircleMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 圈子地理空间服务实现类
 * 实现基于地理坐标的复杂查询和计算
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CircleGeoServiceImpl implements CircleGeoService {

    private final CircleMapper circleMapper;
    private final ObjectMapper objectMapper = new ObjectMapper();

    private static final double EARTH_RADIUS = 6371000; // 地球半径，单位：米

    @Override
    public List<SpotInCircleDto> getSpotsInCircle(Long circleId) {
        try {
            // 1. 查询circle的中心点和多边形边界
            CircleGeoInfoDto circleInfo = circleMapper.selectCircleGeoInfo(circleId);
            if (circleInfo == null) {
                log.warn("Circle not found for circleId: {}", circleId);
                return new ArrayList<>();
            }

            if (circleInfo.getPointsString() == null || circleInfo.getPointsString().trim().isEmpty()) {
                log.warn("Points string is empty for circleId: {}", circleId);
                return new ArrayList<>();
            }

            // 2. 解析多边形顶点坐标
            List<Location> polygon = parsePolygonPoints(circleInfo.getPointsString());
            if (polygon.isEmpty() || polygon.size() < 3) {
                log.warn("Invalid polygon points for circleId: {}", circleId);
                return new ArrayList<>();
            }
            circleInfo.setPolygon(polygon);

            // 3. 计算中心点到多边形顶点的最大距离
            double maxDistance = calculateMaxDistance(
                    circleInfo.getCenterLatitude(), 
                    circleInfo.getCenterLongitude(), 
                    polygon
            );
            circleInfo.setMaxDistance(maxDistance);

            log.debug("Circle {}: center=({},{}) maxDistance={}m polygonSize={}", 
                    circleId, circleInfo.getCenterLatitude(), 
                    circleInfo.getCenterLongitude(), maxDistance, polygon.size());

            // 4. 查询该距离范围内的spot数据（粗筛）
            // 4. 查询该距离范围内的spot数据（粗筛），排除已关联的spot
            List<SpotInCircleDto> spotsInRange = circleMapper.selectSpotsByDistance(
                    circleInfo.getCenterLongitude(),
                    circleInfo.getCenterLatitude(),
                    maxDistance,
                    circleId  // 添加circleId参数，用于排除已关联的spot
            );

            log.debug("Found {} spots within {}m for circle {} (excluding already associated)", 
                    spotsInRange.size(), maxDistance, circleId);

            // 5. 精确筛选：判断spot是否在多边形内部
            List<SpotInCircleDto> spotsInPolygon = new ArrayList<>();
            for (SpotInCircleDto spot : spotsInRange) {
                if (isPointInPolygon(spot.getLatitude(), spot.getLongitude(), polygon)) {
                    spotsInPolygon.add(spot);
                }
            }

            log.info("Successfully found {} spots in circle polygon for circleId: {}", 
                    spotsInPolygon.size(), circleId);
            return spotsInPolygon;

        } catch (Exception e) {
            log.error("Error getting spots in circle for circleId: {}", circleId, e);
            return new ArrayList<>();
        }
    }

    /**
     * 解析JSON格式的多边形顶点坐标
     *
     * @param pointsString JSON字符串格式的坐标数组
     * @return 坐标点列表
     */
    private List<Location> parsePolygonPoints(String pointsString) {
        try {
            // 处理可能的格式问题
            String cleanJson = pointsString.trim();
            if (!cleanJson.startsWith("[") || !cleanJson.endsWith("]")) {
                log.error("Invalid JSON format for polygon points: {}", pointsString);
                return new ArrayList<>();
            }

            List<Location> points = objectMapper.readValue(
                    cleanJson, 
                    new TypeReference<List<Location>>() {}
            );

            // 验证坐标数据
            List<Location> validPoints = new ArrayList<>();
            for (Location point : points) {
                if (point.getLatitude() != null && point.getLongitude() != null &&
                    point.getLatitude() >= -90 && point.getLatitude() <= 90 &&
                    point.getLongitude() >= -180 && point.getLongitude() <= 180) {
                    validPoints.add(point);
                } else {
                    log.warn("Invalid coordinate: lat={}, lng={}", 
                            point.getLatitude(), point.getLongitude());
                }
            }

            return validPoints;
        } catch (Exception e) {
            log.error("Failed to parse polygon points: {}", pointsString, e);
            return new ArrayList<>();
        }
    }

    /**
     * 计算中心点到多边形各顶点的最大距离
     *
     * @param centerLat 中心点纬度
     * @param centerLng 中心点经度
     * @param polygon 多边形顶点坐标列表
     * @return 最大距离（米）
     */
    private double calculateMaxDistance(double centerLat, double centerLng, 
                                        List<Location> polygon) {
        double maxDistance = 0;
        for (Location point : polygon) {
            double distance = calculateDistance(centerLat, centerLng, 
                    point.getLatitude(), point.getLongitude());
            maxDistance = Math.max(maxDistance, distance);
        }
        
        // 添加10%的安全边界
        return maxDistance * 1.1;
    }

    /**
     * 计算两点间的球面距离（使用Haversine公式）
     *
     * @param lat1 点1纬度
     * @param lng1 点1经度
     * @param lat2 点2纬度
     * @param lng2 点2经度
     * @return 两点间距离（米）
     */
    private double calculateDistance(double lat1, double lng1, double lat2, double lng2) {
        // 转换为弧度
        double radLat1 = Math.toRadians(lat1);
        double radLng1 = Math.toRadians(lng1);
        double radLat2 = Math.toRadians(lat2);
        double radLng2 = Math.toRadians(lng2);

        // Haversine公式
        double deltaLat = radLat2 - radLat1;
        double deltaLng = radLng2 - radLng1;

        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                   Math.cos(radLat1) * Math.cos(radLat2) *
                   Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return EARTH_RADIUS * c;
    }

    /**
     * 判断点是否在多边形内部（射线法/奇偶规则）
     *
     * @param pointLat 点的纬度
     * @param pointLng 点的经度
     * @param polygon 多边形顶点坐标列表
     * @return 如果点在多边形内部返回true，否则返回false
     */
    private boolean isPointInPolygon(double pointLat, double pointLng, 
                                   List<Location> polygon) {
        if (polygon.size() < 3) {
            log.warn("Polygon has less than 3 vertices: {}", polygon.size());
            return false;
        }

        boolean inside = false;
        int n = polygon.size();

        for (int i = 0, j = n - 1; i < n; j = i++) {
            double latI = polygon.get(i).getLatitude();
            double lngI = polygon.get(i).getLongitude();
            double latJ = polygon.get(j).getLatitude();
            double lngJ = polygon.get(j).getLongitude();

            // 检查点是否在边界上（考虑浮点精度）
            if (isPointOnBoundary(pointLat, pointLng, latI, lngI, latJ, lngJ)) {
                return true;
            }

            // 射线法判断
            if (((latI > pointLat) != (latJ > pointLat)) &&
                (pointLng < (lngJ - lngI) * (pointLat - latI) / (latJ - latI) + lngI)) {
                inside = !inside;
            }
        }

        return inside;
    }

    /**
     * 判断点是否在线段边界上（考虑浮点精度）
     *
     * @param pointLat 点的纬度
     * @param pointLng 点的经度
     * @param segLat1 线段端点1纬度
     * @param segLng1 线段端点1经度
     * @param segLat2 线段端点2纬度
     * @param segLng2 线段端点2经度
     * @return 如果点在线段边界上返回true
     */
    private boolean isPointOnBoundary(double pointLat, double pointLng,
                                    double segLat1, double segLng1,
                                    double segLat2, double segLng2) {
        // 简化的边界判断，使用距离阈值
        double distance = pointToLineDistance(pointLat, pointLng, 
                                            segLat1, segLng1, segLat2, segLng2);
        return distance < 2.0; // 2米阈值
    }

    /**
     * 计算点到线段的距离
     */
    private double pointToLineDistance(double pointLat, double pointLng,
                                     double lineLat1, double lineLng1,
                                     double lineLat2, double lineLng2) {
        // 使用向量投影法计算点到线段的距离
        double A = pointLat - lineLat1;
        double B = pointLng - lineLng1;
        double C = lineLat2 - lineLat1;
        double D = lineLng2 - lineLng1;

        double dot = A * C + B * D;
        double lenSq = C * C + D * D;

        if (lenSq == 0) {
            return calculateDistance(pointLat, pointLng, lineLat1, lineLng1);
        }

        double param = Math.max(0, Math.min(1, dot / lenSq));

        double projLat = lineLat1 + param * C;
        double projLng = lineLng1 + param * D;

        return calculateDistance(pointLat, pointLng, projLat, projLng);
    }
}